package net.alche.fa.core;

// import net.alche.fa.biz.SageBiz;
import java.util.List;
import net.alche.fa.core.model.Logic;
import java.util.Map;
import net.alche.fa.core.model.FaValue;
import java.util.HashMap;
import net.alche.fa.core.doing.AbstractDoing;
import net.alche.fa.core.doing.DotDo;
import java.util.Map.Entry;

//
public class SageStone {
  FaPlatform plat;
  FaClass faClass;
  SageStone _this;
  SageStone _outer; // 内部类的父类
  // @JsonIgnore
  // SageBiz sageBiz;
  List<Logic> logicList;
  Map<String, FaValue> vars = new HashMap<>();
  List<AbstractDoing> doingList = null;
  // boolean isDebug = true
  // public static Map<String, Boolean> pointMap = new HashMap<>()
  // private static final int BREAK_STEP_CONTINUE = 1
  // private static final int BREAK_STEP_OVER = 2
  // private static long breakThreadId = 0
  // private static int breakPointCtrl = BREAK_STEP_CONTINUE
  // private static boolean isSleeping = false
  // public static SageStone sleepingSageStone = null
  private boolean canNullVar; // 定义的变量可以不存在

  public SageStone(FaPlatform plat, List<Logic> logicList, FaClass fac) {
    this.plat = plat;
    this.doingList = plat.getDoingList();
    this.logicList = logicList;
    if (fac != null) {
      this._this = fac.getThisSage();
    }
    this.faClass = fac;
    this.canNullVar = false;
  }

  public void putVar(String key, FaValue value) {
    this.vars.put(key, value);
    value.setVarName(key);
  }

  public void removeVar(String key) {
    vars.remove(key);
  }

  public FaValue run(CodeLevel cl) throws FaException {
    if (cl == null) {
      cl = new CodeLevel();
    }
    return runCode(logicList, cl);
  }

  public FaValue run(List<Logic> gicList, CodeLevel cl) throws FaException {
    if (cl == null) {
      cl = new CodeLevel();
    }
    return runCode(gicList, cl);
  }

  // 核心运行方法
  // @param g
  // @param lvl
  // @return
  public CodeLevel runCode(Logic g, CodeLevel lvl) throws FaException {
    if (lvl == null) {
      throw new FaException(FaException.NULL_ERROR, "lvl is null");
    }
    lvl.doChild = true;
    if (g == null) {
      throw new FaException(FaException.NULL_ERROR, "logic is null");
    }
    // 是否debug
    if (g.getNum() != null && this.getFaClass() != null && this.getFaClass().getConf() != null) {
      if (this.plat.getBreakPoints() != null && !this.plat.getBreakPoints().isEmpty()) {
        if (this.plat.isBreakPoint(this.getFaClassName(), g.getNum())) {
          this.plat.toBreakPointing(this, lvl, g.getNum());
        } else if (this.plat.getBreakPointCtrl() == FaPlatform.BREAK_STEP_OVER) {
          this.plat.toBreakPointing(this, lvl, g.getNum());
        }
      }
      if (this.plat.getBreakPointCtrl() == FaPlatform.BREAK_STOP) {
        throw new FaException(FaException.USER_STOP, "用户停止运行!");
      }
    }
    // what
    if (g.getWhat() != null || g.getObj() != null) {
      if (lvl.debugFall != null) {
        if (!lvl.isException) {
          lvl.debugFall.setLogic(g);
        }
      }
      for (AbstractDoing doing : doingList) {
        if (doing.matched(g.getWhat(), g)) {
          try {
            FaValue ov = doing.doing(g, lvl, this);
            if (lvl.debugFall != null) {
              lvl.debugFall.setLogic(g);
            }
            lvl.returnObj = ov;
            break;
          } catch (Exception ex) {
            FaException e = null;
            if (ex instanceof FaException) {
              e = (FaException) ex;
            } else {
              e = new FaException(ex);
              e.setCodeLevel(lvl);
              e.printStackTrace();
            }
            if (g.getNum() != null && e.getLineNum() == null) {
              e.setLineNum("" + g.getNum());
              e.setCoderTxt(g.getCodeStr());
            }
            if (e.getDebugFall() == null) {
              e.setDebugFall(lvl.debugFall);
            }
            throw e;
          }
        }
      }
    }
    if (lvl.isReturn || lvl.isContinue || lvl.isBreak) {
      return lvl;
    }
    if (lvl.doChild && g.getChild() != null && !g.getChild().isEmpty()) {
      CodeLevel lv = lvl.instanceDeep();
      for (Logic gg : g.getChild()) {
        CodeLevel rlv = runCode(gg, lv);
        if (rlv.isReturn || rlv.isBreak || rlv.isContinue) {
          return rlv;
        }
      }
    }
    return lvl;
  }

  public FaValue runCode(List<Logic> logicList, CodeLevel cl) throws FaException {
    Logic root = new Logic();
    root.setChild(logicList);
    if (doingList == null) {
      this.doingList = this.plat.getDoingList();
    }
    try {
      CodeLevel rlv = runCode(root, cl);
      if (rlv != null) {
        cl.returnObj = rlv.returnObj;
        cl.isBreak = rlv.isBreak;
        cl.isContinue = rlv.isContinue;
        cl.isReturn = rlv.isReturn;
        return rlv.returnObj;
      }
    } catch (FaException ex) {
      throw ex;
    }
    return null;
  }

  public FaValue runCodeValue(Logic g, CodeLevel lvl) throws FaException {
    CodeLevel cl = runCode(g, lvl);
    return cl.returnObj;
  }

  public FaValue getVar(String obj) {
    FaValue fav = vars.get(obj);
    if (fav == null && this._this != null) {
      fav = this._this.getVar(obj);
    }
    if (fav == null && this._outer != null) {
      fav = this._outer.getVar(obj);
    }
    return fav;
  }

  public FaValue getVarData(String obj, CodeLevel lvl) throws FaException {
    if (obj == null) {
      throw new FaException("getVarData less param", obj);
    }
    try {
      if (obj.equals("this")) {
        FaValue fav = new FaValue();
        fav.setNull(false);
        fav.setSageValue(this._this);
        fav.setObjType(this.faClass.conf.name);
        fav.setType(FaValue._SAGE);
        return fav;
      }
      if (obj.equals("class")) {
        return new FaValue(this.getFaClass());
      }
      if (obj.startsWith("$")) {
        return this.plat.getFaDo().getStaticObj(obj, lvl);
      }
      if (obj.contains(".")) {
        String[] obSp = obj.split("\\.");
        FaValue fav = this.getVarData(obSp[0], lvl);
        for (int i = 1; i < obSp.length; i++) {
          if (fav == null) {
            return new FaValue();
          }
          fav = DotDo.dotDo(this.plat, fav, obSp[i], null, null);
        }
        return fav;
      }
      if ("null".equals(obj)) {
        return new FaValue(true, obj);
      }
      FaValue fav = vars.get(obj);
      if (fav == null) {
        if (this._this != null) { // && !this._this.faClass.conf.name.equals("Object")
          fav = this._this.getVar(obj);
        }
        if (fav == null && this._outer != null) {
          fav = this._outer.getVar(obj);
        }
        if (fav != null) {
          return fav;
        }
        // fav = FaDo.getStaticFav(obj)
        // if fav != null
        // return fav
        if (this.getFaClass() != null && !this.getFaClass().isStatic()) {
          FaValue staticVal = this.getFaClass().getStaticVal(obj, lvl);
          if (staticVal != null) {
            return staticVal;
          }
        }
        if (this.canNullVar) {
          fav = new FaValue();
        } else {
          throw new FaException(FaException.NO_DEFINE_VAR, "[SageStone.getVarData] " + obj);
        }
      }
      return fav;
    } catch (NullPointerException e) {
      e.printStackTrace();
      throw new FaException(FaException.NULL_ERROR, "ofr.getVarData(" + obj + ")");
    }
  }

  public void fillVars(Map<String, FaValue> vars) {
    for (Entry<String, FaValue> en : vars.entrySet()) {
      this.vars.put(en.getKey(), en.getValue());
    }
  }

  public String getFaClassName() {
    if (this.faClass != null) {
      return this.faClass.getConf().getName();
    } else if (this._this != null) {
      return this._this.getFaClassName();
    }
    return "";
  }

  public Map<String, Object> toJavaMap() {
    Map<String, Object> map = new HashMap<>();
    if (!this.getVars().isEmpty()) {
      for (Entry<String, FaValue> en : this.getVars().entrySet()) {
        FaValue fav = en.getValue();
        if (fav != null && !fav.isNull()) {
          map.put(en.getKey(), fav.getJava());
        }
      }
    }
    return map;
  }

  public void log(Object log) {
    System.out.println(log);
  }

  public SageStone get_this() {
    return _this;
  }

  public void set_this(SageStone _this) {
    this._this = _this;
  }

  public void setOuter(SageStone out) {
    this._outer = out;
  }

  // public SageBiz getSageBiz() {
  // return sageBiz;
  // }

  // public void setSageBiz(SageBiz sageBiz) {
  // this.sageBiz = sageBiz;
  // }

  public FaClass getFaClass() {
    return faClass;
  }

  public Map<String, FaValue> getVars() {
    return vars;
  }

  public void setCanNull(boolean isCan) {
    this.canNullVar = isCan;
  }

  public FaPlatform getPlat() {
    return this.plat;
  }

  public void setPlat(FaPlatform plat) {
    this.plat = plat;
  }
}